package com.example.demo.http;


import com.alibaba.fastjson.JSONObject;
import com.example.demo.util.Aes;
import com.example.demo.util.StringUtil;
import org.apache.http.*;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.*;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.AbstractHttpMessage;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.MimeTypeUtils;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.file.FileAlreadyExistsException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class HttpClient {

    private static final Logger log = LoggerFactory.getLogger(HttpClient.class);

    private PoolingHttpClientConnectionManager cm;

    private CloseableHttpClient closeableHttpClientClient; //InternalHttpClient

    private SocketConfig socketConfig;

    private RequestConfig requestConfig;

    private HttpHost proxy;
    /**
     * 默认的请求头
     */
    private String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36";

    private String UTF8 = "UTF-8";

    private ContentType textContentType = ContentType.create(MimeTypeUtils.TEXT_PLAIN_VALUE, Consts.UTF_8);

    public HttpClient() {
        super();
        init();
    }

    private void init() {
//        cm = new PoolingHttpClientConnectionManager();
        cm = HttpConnectionPoolManager.getInstance().getPoolingHttpClientConnectionManager();
        socketConfig = SocketConfig.custom().setSoKeepAlive(true).build();
        cm.setDefaultSocketConfig(socketConfig);
        /**
         * 60秒超时控制
         */
        int timeOut = 60 * 1000;
        requestConfig = RequestConfig
                .custom()
                .setCookieSpec(CookieSpecs.STANDARD_STRICT)
                .setConnectTimeout(timeOut)
                .setConnectionRequestTimeout(timeOut)
                .setSocketTimeout(timeOut)
                .build();
        HttpClientBuilder httpClientBuilder = HttpClients.custom()
                .setConnectionManager(cm)
                //使用长连接(默认的长连接策略)
                .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                //请求配置
                .setDefaultRequestConfig(requestConfig)
                //重试策略
                .setRetryHandler(HttpRequestRetryHandlerManager.REQUEST_RETRY_HANDLER);
        //设置代理
        //proxy = new HttpHost("127.0.0.1",8888);
        //  httpClientBuilder.setProxy(proxy);
        closeableHttpClientClient = httpClientBuilder.build();
    }


    //==================POST=====================POST==========================POST=======================POST==========

    /**
     * 模拟form表单提交
     *
     * @param url           请求路径
     * @param requestHeader 请求头
     * @param textBody      请求提交的普通字段
     * @param fileBody      请求提交的文件
     * @return 返回的字符串，若返回的状态码为 200 才会有字符串返回，否则返回的都是 null
     */
    public String doPostFromArr(String url, Map<String, String> requestHeader, Map<String, List<String>> textBody, Map<String, List<ContentBody>> fileBody) {
        HttpPost httpPost = new HttpPost(url);
        setRequestHeader(requestHeader, httpPost);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        setModeCharsetContentBody(fileBody, builder);
        if (textBody != null) {
            textBody.keySet().forEach(key -> {
                List<String> values = textBody.get(key);
                if (values != null && values.size() > 0) {
                    values.forEach(value -> builder.addTextBody(key, value, textContentType));
                }
            });
        }
        return getResult(httpPost, builder);
    }

    private String getResult(HttpPost httpPost, MultipartEntityBuilder builder) {
        HttpEntity requestHttpEntity = builder.build();
        httpPost.setEntity(requestHttpEntity);
        HttpEntity responseHttpEntity = null;
        CloseableHttpResponse response = null;
        try {
            response = closeableHttpClientClient.execute(httpPost);
            responseHttpEntity = response.getEntity();
            // 判断返回状态是否为200
            int statusCode = response.getStatusLine().getStatusCode();
            String result = EntityUtils.toString(responseHttpEntity, UTF8);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            //释放输入流
            if (responseHttpEntity != null) {
                EntityUtils.consumeQuietly(responseHttpEntity);
            }
            //关闭response
            IOUtils.closeQuietly(response);
            //释放连接
            if (httpPost != null) {
                httpPost.releaseConnection();
            }
        }
    }

    private void setRequestHeader(Map<String, String> headers, AbstractHttpMessage http) {
        if (headers != null) {
            headers.keySet().forEach(key -> http.setHeader(key, headers.get(key)));
        }
    }


    private void setModeCharsetContentBody(Map<String, List<ContentBody>> contentBody, MultipartEntityBuilder builder) {
        builder.setMode(HttpMultipartMode.RFC6532);
        builder.setCharset(Charset.forName(UTF8));
        if (contentBody != null && contentBody.keySet().size() > 0) {
            contentBody.keySet().forEach(key -> {
                List<ContentBody> cbs = contentBody.get(key);
                if (cbs != null && cbs.size() > 0) {
                    cbs.forEach(body -> builder.addPart(key, body));
                }
            });
        }
    }

    /**
     * 模拟form表单提交
     *
     * @param url           请求路径
     * @param requestHeader 请求头
     * @param textBody      请求提交的普通字段
     * @param contentBody   请求提交的文件
     * @return 返回的字符串，若返回的状态码为 200 才会有字符串返回，否则返回的都是 null
     */
    public String doPostFrom(String url, Map<String, String> requestHeader, Map<String, String> textBody, Map<String, List<ContentBody>> contentBody) {
        HttpPost httpPost = new HttpPost(url);
        setRequestHeader(requestHeader, httpPost);
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        setModeCharsetContentBody(contentBody, builder);
        if (textBody != null) {
            textBody.keySet().forEach(key -> {
                builder.addTextBody(key, textBody.get(key), textContentType);
            });
        }
        return getResult(httpPost, builder);
    }


    public String doPostFrom(String url, Map<String, String> textBody, Map<String, List<ContentBody>> contentBody) {
        return doPostFrom(url, null, textBody, contentBody);
    }

    public String doPostFrom(String url, Map<String, String> textBody) {
        return doPostFrom(url, null, textBody, null);
    }

    public String doPostFrom(String url) {
        return doPostFrom(url, null, null, null);
    }

    /**
     * 普通的post请求
     */
    public String doPostArr(String url, Map<String, String> headers, Map<String, List<String>> params, String charset) {
        HttpPost httpPost = new HttpPost(url);
        setRequestHeader(headers, httpPost);
        UrlEncodedFormEntity entity = null;
        CloseableHttpResponse response = null;
        HttpEntity responseEntity = null;
        try {
            // 创建参数列表
            if (params != null && params.size() > 0) {
                List<NameValuePair> paramList = new ArrayList<>();
                for (String key : params.keySet()) {
                    List<String> values = params.get(key);
                    if (values != null && values.size() > 0) {
                        values.forEach(value -> paramList.add(new BasicNameValuePair(key, value)));
                    }
                }
                // 模拟表单(内部会进行url编码)
                entity = new UrlEncodedFormEntity(paramList, charset);
                httpPost.setEntity(entity);
            }
            // 执行http请求
            response = closeableHttpClientClient.execute(httpPost);
            // 判断返回状态是否为200
            responseEntity = response.getEntity();
            // 判断返回状态是否为200
            int statusCode = response.getStatusLine().getStatusCode();
            String result = EntityUtils.toString(responseEntity, charset);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            //释放输入流
            if (entity != null) {
                EntityUtils.consumeQuietly(entity);
            }
            if (responseEntity != null) {
                EntityUtils.consumeQuietly(responseEntity);
            }
            //关闭response
            IOUtils.closeQuietly(response);
            //释放连接
            if (httpPost != null) {
                httpPost.releaseConnection();
            }
        }
    }

    /**
     * 普通的post请求
     */
    public String doPost(String url, Map<String, String> headers, Map<String, String> params, String charset) {
        HttpPost httpPost = new HttpPost(url);
        setRequestHeader(headers, httpPost);
        UrlEncodedFormEntity entity = null;
        CloseableHttpResponse response = null;
        HttpEntity responseEntity = null;
        try {
            // 创建参数列表
            if (params != null) {
                List<NameValuePair> paramList = new ArrayList<NameValuePair>();
                for (String key : params.keySet()) {
                    paramList.add(new BasicNameValuePair(key, params.get(key)));
                }
                // 模拟表单(内部会进行url编码)
                entity = new UrlEncodedFormEntity(paramList, charset);
                httpPost.setEntity(entity);
            }
            // 执行http请求
            response = closeableHttpClientClient.execute(httpPost);
            // 判断返回状态是否为200
            responseEntity = response.getEntity();
            // 判断返回状态是否为200
            int statusCode = response.getStatusLine().getStatusCode();
            String result = EntityUtils.toString(responseEntity, charset);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            //释放输入流
            if (entity != null) {
                EntityUtils.consumeQuietly(entity);
            }
            if (responseEntity != null) {
                EntityUtils.consumeQuietly(responseEntity);
            }
            //关闭response
            IOUtils.closeQuietly(response);
            //释放连接
            if (httpPost != null) {
                httpPost.releaseConnection();
            }
        }
    }

    public String doPost(String url, Map<String, String> params, String charset) {
        return doPost(url, null, params, charset);
    }

    public String doPost(String url, Map<String, String> headers, Map<String, String> params) {
        return doPost(url, headers, params, UTF8);
    }

    public String doPost(String url, Map<String, String> params) {
        return doPost(url, null, params, UTF8);
    }

    public String doPost(String url) {
        return doPost(url, null, null, UTF8);
    }


    /**
     * json字符串的方式提交
     */
    public String doPostStringBody(String url, Map<String, String> headers, String body, ContentType contentType, String charset) {
        // 创建Http Post请求
        HttpPost httpPost = new HttpPost(url);
        setRequestHeader(headers, httpPost);
        // 创建请求内容
        StringEntity entity = new StringEntity(body, contentType);
        httpPost.setEntity(entity);
        // 执行http请求
        CloseableHttpResponse response = null;
        HttpEntity responseHttpEntity = null;
        try {
            response = closeableHttpClientClient.execute(httpPost);
            // 判断返回状态是否为200
            responseHttpEntity = response.getEntity();
            // 判断返回状态是否为200
            int statusCode = response.getStatusLine().getStatusCode();
            String result = EntityUtils.toString(responseHttpEntity, charset);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            if (responseHttpEntity != null) {
                EntityUtils.consumeQuietly(responseHttpEntity);
            }
            IOUtils.closeQuietly(response);
            if (httpPost != null) {
                httpPost.releaseConnection();
            }
        }
    }

    public String doPostStringBody(String url, Map<String, String> headers, String body) {
        return doPostStringBody(url, headers, body, ContentType.APPLICATION_JSON, UTF8);
    }

    public String doPostStringBody(String url, String body, ContentType contentType) {
        return doPostStringBody(url, null, body, contentType, UTF8);
    }

    public String doPostStringBody(String url, String body) {
        return doPostStringBody(url, null, body, ContentType.APPLICATION_JSON, UTF8);
    }


    //==================GET=====================GET==========================GET=======================GET==============

    /**
     * GET 方式请求
     *
     * @param url     请求路径
     * @param params  参数
     * @param headers 请求头
     * @param charset 字符集
     * @return
     */
    public String doGet(String url, Map<String, String> params, Map<String, String> headers, String charset) {
        try {
            URI uri;
            URIBuilder builder = new URIBuilder(url);
            if (params != null) {
                params.keySet().forEach(key -> builder.addParameter(key, params.get(key)));
            }
            uri = builder.build();
            HttpGet httpGet = new HttpGet(uri);
            setRequestHeader(headers, httpGet);
            CloseableHttpResponse response = closeableHttpClientClient.execute(httpGet);
            HttpEntity responseHttpEntity = response.getEntity();
            // 判断返回状态是否为200
            int statusCode = response.getStatusLine().getStatusCode();
            String res = EntityUtils.toString(responseHttpEntity, charset);
            //释放输入流
            if (responseHttpEntity != null) {
                EntityUtils.consumeQuietly(responseHttpEntity);
            }
            //关闭response
            IOUtils.closeQuietly(response);
            //释放连接
            httpGet.releaseConnection();
            return res;
        } catch (URISyntaxException | IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public String doGet(String url, Map<String, String> params, Map<String, String> headers) {
        return doGet(url, params, headers, UTF8);
    }

    public String doGet(String url, Map<String, String> params, String charset) {
        return doGet(url, params, null, charset);
    }

    public String doGet(String url, Map<String, String> params) {
        return doGet(url, params, null, UTF8);
    }

    //==================DOWNLOAD=====================DOWNLOAD==========================DOWNLOAD=========================

    /**
     * 使用GET请求下载文件
     *
     * @param url      文件路径
     * @param filePath 保存下来的文件路径
     * @param cover    是否进行覆盖  true 覆盖 ，false 不覆盖
     * @return
     */
    public void downloadFile(String url, String filePath, Map<String, String> headers, boolean cover) {
        log.debug("下载文件名路径：{}", url);
        HttpGet httpGet = new HttpGet(url);
        setRequestHeader(headers, httpGet);
        File file = new File(filePath);
        CloseableHttpResponse chr = null;
        HttpEntity httpEntity = null;
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        BufferedInputStream bis = null;
        InputStream is = null;
        try {
            boolean exists = file.exists();
            if (!cover && exists) {
                throw new FileAlreadyExistsException("该目录已经存在");
            }
            if (!exists) {
                boolean newFile = file.createNewFile();
                log.debug("创建文件结果：{}", newFile ? "成功" : "失败");
            }
            chr = closeableHttpClientClient.execute(httpGet);
            HttpEntity entity = chr.getEntity();
            Header contentEncoding = entity.getContentEncoding();
            Header contentType = entity.getContentType();
            long contentLength = entity.getContentLength();
            log.debug("文件响应头信息：Content-Length:{}\tContent-Type:{}\tContent-Encoding:{}", contentLength, contentType, contentEncoding);
            //获取输入流
            is = entity.getContent();
            //输入缓冲流
            bis = new BufferedInputStream(is);
            //文件输出流
            fos = new FileOutputStream(file);
            //输出缓冲流
            bos = new BufferedOutputStream(fos);
            int len;
            int byteMaxLen = 4096;
            byte[] buf = new byte[byteMaxLen];
            //达末尾则返回-1
            while ((len = bis.read(buf, 0, byteMaxLen)) != -1) {
                bos.write(buf, 0, len);
            }
            httpEntity = chr.getEntity();
            // 判断返回状态是否为200
//            int statusCode = chr.getStatusLine().getStatusCode();
//            if (statusCode != 200){
//                return false;
//            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            //关流
            IOUtils.closeQuietly(bos);
            IOUtils.closeQuietly(fos);
            IOUtils.closeQuietly(bis);
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(chr);
            //释放输入流
            if (httpEntity != null) {
                EntityUtils.consumeQuietly(httpEntity);
            }
            //释放连接
            httpGet.releaseConnection();
        }
    }

    public void downloadFile(String url, String filePath) {
        downloadFile(url, filePath, null, true);
    }


    //======================================获取ContentBody的几种方式====================================================
    public ContentBody toContentBody(File file) {
        return new FileBody(file, ContentType.DEFAULT_BINARY, file.getName());
    }

    public ContentBody toContentBody(byte[] data, String filename) {
        return new ByteArrayBody(data, ContentType.APPLICATION_OCTET_STREAM, filename);
    }

    public ContentBody toContentBody(InputStream in, String filename) {
        return new InputStreamBody(in, ContentType.DEFAULT_BINARY, filename);
    }

    public ContentBody toContentBody(String text, ContentType contentType) {
        return new StringBody(text, contentType);
    }

    public static void main(String[] args) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("datetime","2020-06-19 14:08:19");
        jsonObject.put("pageNumber",1);
        jsonObject.put("filterDelete",true);
        jsonObject.put("pageSize",5);
        String param = jsonObject.toJSONString();
        HttpClient httpClient = new HttpClient();
        String url = "http://125.69.82.54:11602/dsfa/teas/pub/query/cl";
//        String url = "http://192.168.0.68:82/dsfa/teas/pub/query/cl";
        System.out.println(param);
        String _param = Aes.encrypt(param, "6f3d9686441d435f88b838b855750258609469c72d3246ee8f509aded07301bb");
        System.out.println(_param);
        Map<String,String> map = new HashMap<>();
        map.put("_param", _param);
        String result = httpClient.doGet(url, map);
        System.out.println(result);

    }

}
